// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: linchuan

#pragma once

#include <memory>
#include <regex>
#include <string>
#include <unordered_map>
#include <unordered_set>

#include "coin2/base/string_util.h"
#include "coin2/exchange/base/symbology/currency.h"
#include "coin2/exchange/base/symbology/util.h"

namespace coin2::exchange::kraken::symbology {

static const int64_t LUNA2_LIST_TIME = 1653782400000000000L; //TimestampFromDatetime (2022,05,29)

class KrakenCurrency : public base::symbology::Currency,
                       public base::symbology::CurrencyFromStrMixin<KrakenCurrency> {
 public:
  using ExchangeType = base::market::ExchangeType;
  using MarketType = base::market::MarketType;
  using Currency = base::symbology::Currency;
  using base::symbology::Currency::native_currency;

 public:
  explicit KrakenCurrency(const std::string& currency)
      : Currency(currency, ToNativeCurrency(currency).value(), ExchangeType::Kraken) {}

  static std::unique_ptr<KrakenCurrency> FromNativeStrImpl(
      const std::string& native_currency_str,
      int64_t resolve_timestamp) {
    auto currency = FromNativeCurrency(native_currency_str, resolve_timestamp);
    if (currency) {
      return std::make_unique<KrakenCurrency>(currency.value());
    }
    return {};
  }

  static std::unique_ptr<KrakenCurrency> FromNormStrImpl(
      const std::string& currency_str,
      int64_t resolve_timestamp) {
    auto native_currency = ToNativeCurrency(currency_str);
    if (!native_currency) {
      return {};
    }

    auto norm_currency = FromNativeCurrency(*native_currency, resolve_timestamp);
    if (!norm_currency) {
      return {};
    }
    return std::make_unique<KrakenCurrency>(norm_currency.value());
  }

 private:
  static std::optional<std::string> ToNativeCurrency(const std::string& currency_str) {
    auto currency_str_upper = StringToUpperCopy(currency_str);
    if (currency_str_upper == "BTC") {
      return "XXBT";
    }
    else if (currency_str_upper == "LUNC"){
      return "LUNA";
    }
    

    if (currency_str_upper == "BCHABC" || currency_str_upper == "BCHN") {
      return "BCH";
    }

    if (currency_str_upper == "DOGE") {
      return "XXDG";
    }

    auto ToCurrency = [](std::string currency) {
      if (extra_x.count(currency)) {
        return std::string("X") + currency;
      }

      if (extra_z.count(currency)) {
        return std::string("Z") + currency;
      }

      return currency;
    };

    currency_str_upper = ToCurrency(currency_str_upper);
    return currency_str_upper;
  }

  static std::optional<std::string> FromNativeCurrency(
      const std::string& native_currency_str,
      int64_t resolve_timestamp) {
    const auto native_currency_str_upper = StringToUpperCopy(native_currency_str);

    if (native_currency_str_upper == "BCH") {
      if (resolve_timestamp >= base::symbology::BCHA_BCHN_FORK_TIME) {
        return "BCHN";
      } else {
        return "BCHABC";
      }
    }
    else if (native_currency_str_upper == "LUNA" && resolve_timestamp >= LUNA2_LIST_TIME) {
      return "LUNC";
    }

    if (native_currency_str_upper == "XXDG") {
      return "DOGE";
    }

    static std::unordered_set<std::string> exception_currency = {"ALCX", "CVX", "IMX", "XRT"};

    if (exception_currency.count(native_currency_str) > 0) {
      return native_currency_str;
    }

    static const std::regex reg(
        "^(X|Z)?(AVAX|CHZ|DYDX|ICX|SNX|TRX|XBT|XDG|XLM|XMR|XRP|XTZ|ZEC|ZRX|IDEX|ADX|LCX|BSX|BLZ|XCN|STX|HDX|GMX)(X|Z)?$");
    std::smatch match;
    bool res = std::regex_match(native_currency_str, match, reg);
    if (res) {
      if (match[2] == "XBT") {
        return "BTC";
      } else {
        return match[2];
      }
    }

    const auto len = native_currency_str.size();
    // In case 'X|Z' + CoinName + 'X|Z'
    if (native_currency_str.front() == 'X' || native_currency_str.front() == 'Z') {
      if (native_currency_str.back() == 'X' || native_currency_str.back() == 'Z') {
        // has both prefix and postfix
        return native_currency_str.substr(1, len - 2);
      } else {
        // only has prefix
        return native_currency_str.substr(1, len - 1);
      }
    }

    // In case CoinName + 'X|Z'
    if (native_currency_str.back() == 'X' || native_currency_str.back() == 'Z') {
      // only has postfix
      return native_currency_str.substr(0, len - 1);
    }

    return native_currency_str;
  }

  static std::unordered_set<std::string> extra_x;
  static std::unordered_set<std::string> extra_z;
};

}  // namespace coin2::exchange::kraken::symbology
